#include "GizmoRenderPass.h"
#include <SERender/Renderer/Renderer.h>
#include <SEEdit/Render/RenderFeature/DebugRenderFeature.h>
#include <SEEdit/Descriptors/SelectActorDescriptor.h>
#include <SEngine/Render/Descriptors/SceneDescriptor.h>
#include <SECore/Ecs/Actor.h>
#include <SEngine/Ecs/Components/TransformComp.h>
#include <SERender/Descriptors/FrameDescriptor.h>
#include <array>
#include <SEngine/Ecs/Components/LightComp.h>
#include <SEngine/Ecs/Components/TransformComp.h>
#include <SEngine/Ecs/Components/UuidComp.h>
#include <SEngine/Ecs/Components/CameraComp.h>
#include <SECore/Ecs/Scene.h>
#include <SECore/CoreApplication.h>
#include <SEEdit/Descriptors/ControlBarStateDescriptor.h>

#include <SERender/Resourses/Loader/TextureLoader.h>

namespace SEngine
{
    GizmoRenderPass::GizmoRenderPass(Renderer &renderer)
        : RenderPass(renderer) 
    {
        // FrameBufferLoader loader;
        TextureLoader<Texture2D> texLoader;
        texLoader.SetInternalFormat(RED);
        texLoader.SetFormat(RED);
        texLoader.SetType(UNSIGNED_BYTE);
        texLoader.SetParameter(TEXTURE_MAG_FILTER, LINEAR);
        texLoader.SetParameter(TEXTURE_MIN_FILTER, LINEAR);
        texLoader.SetParameter(TEXTURE_WRAP_S, CLAMP_TO_EDGE);
        texLoader.SetParameter(TEXTURE_WRAP_T, CLAMP_TO_EDGE);
        m_idMap = texLoader.CreateTexture(800, 600, nullptr);
        m_idFBO = std::make_unique<FrameBuffer>();
        m_idFBO->SwapAttachments(Attachment::COLOR_ATTACHMENT0, *m_idMap);

        m_lightMap = texLoader.CreateTexture(800, 600, nullptr);
        m_lightFBO = std::make_unique<FrameBuffer>();
        m_lightFBO->SwapAttachments(Attachment::COLOR_ATTACHMENT0, *m_lightMap);
    }

    void GizmoRenderPass::BeginFrame()
    {
        auto frameDescriptor = m_renderer.AnyDescriptor::GetDescriptor<FrameDescriptor>();
        if (!frameDescriptor) {
            return;
        }
        m_lightFBO->Resize(frameDescriptor->get().w, frameDescriptor->get().h);
        m_lightFBO->Use();
        m_renderer.UseFrameBuffer(*m_lightFBO, [this, &frameDescriptor]() {
            m_renderer.GetDrive()->Viewport(frameDescriptor->get().x, frameDescriptor->get().y, frameDescriptor->get().w, frameDescriptor->get().h);
            m_renderer.GetDrive()->ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
            m_renderer.GetDrive()->Clear(COLOR_BUFFER_BIT | DEPTH_BUFFER_BIT);
        });
        m_idFBO->Resize(frameDescriptor->get().w, frameDescriptor->get().h);
        m_idFBO->Use();
        m_renderer.UseFrameBuffer(*m_idFBO, [this, &frameDescriptor]() {
            m_renderer.GetDrive()->Viewport(frameDescriptor->get().x, frameDescriptor->get().y, frameDescriptor->get().w, frameDescriptor->get().h);
            m_renderer.GetDrive()->ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
            m_renderer.GetDrive()->Clear(COLOR_BUFFER_BIT | DEPTH_BUFFER_BIT);
        });
    }

    void GizmoRenderPass::DrawFrame()
    {
        auto & debugFeature = m_renderer.Descriptor<RenderFeature>::GetDescriptor<DebugRenderFeature>()->get();
        auto & frameDes = m_renderer.AnyDescriptor::GetDescriptor<FrameDescriptor>()->get();
        auto & state = CoreApplication::App().GetDescriptor<ControlBarStateDescriptor>()->get();
        auto & sceneDes = m_renderer.AnyDescriptor::GetDescriptor<SceneDescriptor>()->get();
        
        sceneDes.scene->ComponentsEx([&](Actor& actor, auto & comp) {
            auto transComp = actor.GetComponent<TransformComp>();
            auto uuidComp = actor.GetComponent<UuidComp>();
            const float scale = 0.03f;            
            debugFeature.DrawIcon(comp.Hash(), transComp->get().GetModel(), scale);
            m_renderer.UseFrameBuffer(*m_lightFBO, [&]() {
                m_lightFBO->Use();
                debugFeature.DrawIconID(transComp->get().GetModel(), uuidComp->get().Hash<uint8_t>(), scale);
            });
        }, LightComp{});

        sceneDes.scene->Components<CameraComp, TransformComp, UuidComp>([&](Actor & actor, CameraComp & camera, TransformComp & trans, UuidComp & uuid) {
            const float scale = 0.03f;            
            debugFeature.DrawIcon(camera.Hash(), trans.GetModel(), scale);
            debugFeature.DrawCameraLine(camera.m_camera, (float)frameDes.w, (float)frameDes.h, glm::vec4{glm::vec3{1.0f}, 0.5f}, 1.0f);
            m_renderer.UseFrameBuffer(*m_lightFBO, [&]() {
                m_lightFBO->Use();
                debugFeature.DrawIconID(trans.GetModel(), uuid.Hash<uint8_t>(), scale);
            });
        });

        sceneDes.scene->Components<AmbientLightSphereComp, TransformComp>([&](Actor & actor, AmbientLightSphereComp & comp, TransformComp & trans) {
            debugFeature.DrawCircle(trans.GetTranslateMatrix() * glm::rotate(glm::mat4{1.0f}, glm::radians(0.0f), glm::vec3{1.0f, 0.0f, 0.0f}) * glm::scale(glm::mat4{1.0f}, glm::vec3{comp.light.GetRadius()}), glm::vec4{comp.light.GetColor(), 0.5f}, 2);
            debugFeature.DrawCircle(trans.GetTranslateMatrix() * glm::rotate(glm::mat4{1.0f}, glm::radians(90.0f), glm::vec3{1.0f, 0.0f, 0.0f}) * glm::scale(glm::mat4{1.0f}, glm::vec3{comp.light.GetRadius()}), glm::vec4{comp.light.GetColor(), 0.5f}, 2);
            debugFeature.DrawCircle(trans.GetTranslateMatrix() * glm::rotate(glm::mat4{1.0f}, glm::radians(90.0f), glm::vec3{0.0f, 1.0f, 0.0f}) * glm::scale(glm::mat4{1.0f}, glm::vec3{comp.light.GetRadius()}), glm::vec4{comp.light.GetColor(), 0.5f}, 2);
        });
    }

    ArrowID GizmoRenderPass::GetArrowID(int x, int y)
    {
        ArrowID id = ARROW_NONE; 
        m_renderer.UseFrameBuffer(*m_idFBO, [&]() {
            std::array<uint8_t, 3> buf;
            m_renderer.GetDrive()->ReadPixels(x, y, 1, 1, ColorFormat::RGB, ElemType::UNSIGNED_BYTE, buf.data());
            id = (ArrowID)buf[0];
        });
        return id;
    }

    std::optional<uint8_t> GizmoRenderPass::GetID(int x, int y)
    {
        std::optional<uint8_t> id;
        m_renderer.UseFrameBuffer(*m_lightFBO, [&]() {
            std::array<uint8_t, 3> buf;
            m_renderer.GetDrive()->ReadPixels(x, y, 1, 1, ColorFormat::RGB, ElemType::UNSIGNED_BYTE, buf.data());
            if (buf[0] != 0) {
                id = buf[0];
            }
        });
        return id;
    }

} // namespace SEngine
